const NginxConfFile = require('nginx-conf').NginxConfFile;
const GMCONF = require("./gm_conf");
const shell = require('shelljs')
const fs = require('fs');
var path = require('path');

let SUPPORT_HTTPS = true;
let LOCAL_SERVER_INTER_PORT = "3000";

function createNginxConf(path) {
    console.log("createNginxConf",path);
    return new Promise((resolve, reject) => {
        NginxConfFile.create(path, function(err, conf) {
            if (err) {
              console.log("nginx conf create failed,", err);
              reject(err);
            } else {
              console.log("nginx conf create success");
              resolve(conf);
            }
        });
    });
}

function mkdirsSync(dirname) {
    if (fs.existsSync(dirname)) {
      return true;
    } else {
      if (mkdirsSync(path.dirname(dirname))) {
        fs.mkdirSync(dirname);
        return true;
      }
    }
  }

function randomRange(min, range) {
    let rand = Math.random();
    return min + Math.round(rand * range);
}

function waitFlush(nginx_conf) {
    return new Promise((resolve, reject) => {
        nginx_conf.flush((err)=>{
            if(err) {
                reject(err);
            } else {
                resolve(0);
            }
        });
    });
}

function nginx_reload() {
    let cmd = `${GMCONF.PATH_NGINX_EXE} -s reload`;
    shell.exec(`sleep 2s`).code;
    let exe_ret = shell.exec(cmd);
    if(exe_ret.code != 0) {
        exe_ret = shell.exec(`${GMCONF.PATH_NGINX_EXE} -c ${GMCONF.PATH_NGINX_CONF}`);
    }
    console.log("nginx reload:",exe_ret);
    if(exe_ret.code != 0) {
        if(exe_ret.stderr && exe_ret.stderr.indexOf("tasscard_sm2:30") != -1) {
            return -2;
        } else if(exe_ret.stderr && exe_ret.stderr.indexOf("tasscard_sm2:20") != -1) {
            return -3;
        }
        return exe_ret.code;
    } else {
        return 0;
    }
}

function openssl_lib_source() {
    console.log("source", GMCONF.PATH_CLIENT_SETTING);
    let cmd_ret = shell.exec(`source ${GMCONF.PATH_CLIENT_SETTING}`);
    return cmd_ret;
}

function gen_p10_req(isClient, index, dn, outFile) {
    let cmd = isClient ? GMCONF.PATH_CLIENT_GEN_SH : GMCONF.PATH_SERVER_GEN_SH;
    let exe_cmd = `${cmd} -p -i ${index} -d ${dn} -r ${outFile}`;
    console.log("p10 cmd:", exe_cmd);
    let cmd_ret = shell.exec(exe_cmd).code;
    return cmd_ret;
}

function import_private_key(p7bFile, sign_key_index, enc_key_index) {
    let cmd_ret = shell.exec(`${GMCONF.PATH_TASS_OPENSSL} pkcs7 -in ${p7bFile} -in_sign_key_index ${sign_key_index} -in_enc_key_index ${enc_key_index} -inform GMT009 -engine tasscard_sm2`).code;
    return cmd_ret;
}

var TassCmd2 = TassCmd2 || {};

TassCmd2.tass_init = async() => {
    let exe_ret = shell.exec(GMCONF.PATH_TASS_INIT_SH).code;
    if(exe_ret != 0) {
        return -1;
    }

    return new Promise((resolve, reject) => {
        fs.mkdir(GMCONF.DIR_NGINX_CONFS,function(err){
            if (err) {
                reject(-1);
            } else {
                resolve(0);
            }
         });
    });
}

TassCmd2.gen_csr = function(isClient, index, dn, outFile) {
    let ret = 0;
    do{
        ret = openssl_lib_source();
        if(ret != 0) {
            console.log("error openssl_lib_source:",ret);
            break;
        }
        ret = gen_p10_req(isClient, index, dn, outFile);
        console.log("gen_p10_req:",ret);
    }while(0);
    return ret;
}

TassCmd2.import_files = async function(p7bFile, cert_index, enc_cert_index) {
    let ret = 0;
    do{
        ret = openssl_lib_source();
        if(ret != 0) {
            console.log("error openssl_lib_source:",ret);
            break;
        }
        ret = import_private_key(p7bFile, cert_index, enc_cert_index);
        if(ret != 0) {
            console.log("error import_private_key:",ret);
            break;
        }
    }while(0);
    return ret;
}

TassCmd2.add_new_conf = async function(isroot, remote_ip, remote_port, local_ip, local_client_port, 
    local_server_port, client_ssl_cert, client_cert_index, client_ssl_enc, client_enc_cert_index,
    server_ssl_cert, server_cert_index, server_ssl_enc, server_enc_cert_index) {
    let ret = 0;
    let filename = isroot ? "root" : Date.now() + "_" + randomRange(0, 1000);
    do{
        let path_conf = path.resolve(GMCONF.DIR_NGINX_CONFS, filename+".conf");

        if(fs.existsSync(path_conf)) {
            break;
        }

        mkdirsSync(GMCONF.DIR_NGINX_CONFS);
        fs.writeFileSync(path_conf, "");

        let nginx_conf = await createNginxConf(path_conf);

        let remote_server_val = `${remote_ip}:${remote_port}`;

        nginx_conf.nginx._add('upstream', 'cloudGw'+filename);
        nginx_conf.nginx.upstream._add('server', remote_server_val);

        nginx_conf.nginx._add('server');
        nginx_conf.nginx.server._add('listen', local_client_port);
        nginx_conf.nginx.server._add('proxy_pass', 'cloudGw'+filename);

        nginx_conf.nginx.server._add('include', GMCONF.NGINX_CLIENT_COMMON_CONF);

        ret = await waitFlush(nginx_conf);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }

        if(!fs.existsSync(GMCONF.NGINX_CLIENT_COMMON_CONF)) {
            mkdirsSync(GMCONF.DIR_NGINX_COMMON_CONFS);
            fs.writeFileSync(GMCONF.NGINX_CLIENT_COMMON_CONF, "");

            let common_client = await createNginxConf(GMCONF.NGINX_CLIENT_COMMON_CONF);

            common_client.nginx._add("proxy_ssl", "on");
            common_client.nginx._add("proxy_ssl_gm", "on");
            common_client.nginx._add("proxy_ssl_ciphers", "ECDHE-SM4-SM3");
            common_client.nginx._add("proxy_ssl_certificate", `${client_ssl_cert}`);
            let client_cert_key = `engine:tasscard_sm2:${client_cert_index}`;
            common_client.nginx._add("proxy_ssl_certificate_key", client_cert_key);
            common_client.nginx._add("proxy_ssl_enc_certificate", `${client_ssl_enc}`);
            let client_enc_cert_key = `engine:tasscard_sm2:${client_enc_cert_index}`;
            common_client.nginx._add("proxy_ssl_enc_certificate_key", client_enc_cert_key);
            ret = await waitFlush(common_client);
            if(ret != 0) {
                console.log("error waitFlush:",ret);
                break;
            }
        }

        if(!fs.existsSync(GMCONF.NGINX_SERVER_COMMON_CONF)) {
            mkdirsSync(GMCONF.DIR_NGINX_COMMON_CONFS);
            fs.writeFileSync(GMCONF.NGINX_SERVER_COMMON_CONF, "");

            let common_server = await createNginxConf(GMCONF.NGINX_SERVER_COMMON_CONF);

            common_server.nginx._add("ssl_certificate", `${server_ssl_cert}`);
            let server_cert_key = `engine:tasscard_sm2:${server_cert_index}`;
            common_server.nginx._add("ssl_certificate_key", server_cert_key);
            common_server.nginx._add("ssl_enc_certificate", `${server_ssl_enc}`);
            let server_enc_cert_key = `engine:tasscard_sm2:${server_enc_cert_index}`;
            common_server.nginx._add("ssl_enc_certificate_key", server_enc_cert_key);

            ret = await waitFlush(common_server);
            if(ret != 0) {
                console.log("error waitFlush:",ret);
                break;
            }
        }

        let root_conf = await createNginxConf(GMCONF.PATH_NGINX_CONF);
        if(root_conf.nginx.stream.upstream) {
            root_conf.nginx.stream.upstream.server._value = `${local_ip}:${LOCAL_SERVER_INTER_PORT}`;
        } else {
            root_conf.nginx.stream._add('upstream', 'localGw');
            root_conf.nginx.stream.upstream._add('server', `${local_ip}:${LOCAL_SERVER_INTER_PORT}`);
        }

        if(root_conf.nginx.stream.server) {
            root_conf.nginx.stream.server.listen._value = ""+ local_server_port + " ssl";
            root_conf.nginx.stream.server.proxy_pass._value = 'localGw';
            root_conf.nginx.stream.server.include._value = GMCONF.NGINX_SERVER_COMMON_CONF;
        } else {
            root_conf.nginx.stream._add('server');
            root_conf.nginx.stream.server._add('listen', ""+ local_server_port + " ssl");
            root_conf.nginx.stream.server._add('proxy_pass', 'localGw');
            root_conf.nginx.stream.server._add('include', GMCONF.NGINX_SERVER_COMMON_CONF);
        }

        ret = await waitFlush(root_conf);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }

        
        ret = openssl_lib_source();
        if(ret != 0) {
            console.log("error openssl_lib_source:",ret);
            break;
        }
        ret = nginx_reload();
    }while(0);
    if(ret == 0) {
        return filename;
    } else {
        return null;
    }
}

TassCmd2.edit_conf = async function(filename, remote_ip, remote_port, local_client_port) {
    let path_conf = path.resolve(GMCONF.DIR_NGINX_CONFS, filename+".conf");
    let ret = 0;
    do{
        if(!fs.existsSync(path_conf)) {
            ret = -1;
            break;
        }
        let nginx_conf = await createNginxConf(path_conf);

        let remote_server_val = `${remote_ip}:${remote_port}`;
        nginx_conf.nginx.upstream.server._value = `${remote_server_val}`;
        //nginx_conf.nginx.upstream[1].server._value = `${local_ip}:${LOCAL_SERVER_INTER_PORT}`;

        nginx_conf.nginx.server.listen._value = local_client_port;
        //nginx_conf.nginx.server[1].listen._value = ""+ local_server_port + " ssl";

        ret = await waitFlush(nginx_conf);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }

        ret = openssl_lib_source();
        if(ret != 0) {
            console.log("error openssl_lib_source:",ret);
            break;
        }
        ret = nginx_reload();
    }while(0);
    return ret;
}

TassCmd2.del_conf = async function(filename) {
    let path_conf = path.resolve(GMCONF.DIR_NGINX_CONFS, filename+".conf");
    if(fs.existsSync(path_conf)) {
        fs.unlinkSync(path_conf);
        openssl_lib_source();
        nginx_reload();
    }
    return 0;
}

TassCmd2.query_vpn_stat = async function() {
    let ret = 0;
    do{
        if(!fs.existsSync(GMCONF.NGINX_CLIENT_COMMON_CONF) || !fs.existsSync(GMCONF.NGINX_SERVER_COMMON_CONF)) {
            ret = -1;
            break;
        }

        let common_client = await createNginxConf(GMCONF.NGINX_CLIENT_COMMON_CONF);

        if(common_client.nginx.proxy_ssl && (common_client.nginx.proxy_ssl._value == "on")) {
            ret = 1;
        } else {
            ret = 0;
        }
    }while(0);
    return ret;
}

TassCmd2.start_vpn = async function(client_ssl_cert, client_cert_index, client_ssl_enc, client_enc_cert_index,
    server_ssl_cert, server_cert_index, server_ssl_enc, server_enc_cert_index) {
    let path_conf = GMCONF.NGINX_CLIENT_COMMON_CONF;//path.resolve(GMCONF.DIR_NGINX_CONFS, filename+".conf");
    let ret = 0;
    do{
        if(!fs.existsSync(path_conf) || !fs.existsSync(GMCONF.NGINX_SERVER_COMMON_CONF)) {
            ret = -1;
            break;
        }
        let common_client = await createNginxConf(path_conf);

        if(!common_client.nginx.proxy_ssl) {
            common_client.nginx._add("proxy_ssl", "on");
        } else {
            common_client.nginx.proxy_ssl._value = "on";
        }
        
        if(!common_client.nginx.proxy_ssl_gm) {
            common_client.nginx._add("proxy_ssl_gm", "on");
        } else {
            common_client.nginx.proxy_ssl_gm._value = "on";
        }

        if(!common_client.nginx.proxy_ssl_ciphers) {
            common_client.nginx._add("proxy_ssl_ciphers", "ECDHE-SM4-SM3");
        } else {
            common_client.nginx.proxy_ssl_ciphers._value = "ECDHE-SM4-SM3";
        }

        if(!common_client.nginx.proxy_ssl_certificate) {
            common_client.nginx._add("proxy_ssl_certificate", `${client_ssl_cert}`);
        } else {
            common_client.nginx.proxy_ssl_certificate._value = `${client_ssl_cert}`;
        }

        let client_cert_key = `engine:tasscard_sm2:${client_cert_index}`;
        if(!common_client.nginx.proxy_ssl_certificate_key) {
            common_client.nginx._add("proxy_ssl_certificate_key", client_cert_key);
        } else {
            common_client.nginx.proxy_ssl_certificate_key._value = client_cert_key;
        }

        if(!common_client.nginx.proxy_ssl_enc_certificate) {
            common_client.nginx._add("proxy_ssl_enc_certificate", `${client_ssl_enc}`);
        } else {
            common_client.nginx.proxy_ssl_enc_certificate._value = `${client_ssl_enc}`;
        }

        let client_enc_cert_key = `engine:tasscard_sm2:${client_enc_cert_index}`;
        if(!common_client.nginx.proxy_ssl_enc_certificate_key) {
            common_client.nginx._add("proxy_ssl_enc_certificate_key", client_enc_cert_key);
        } else {
            common_client.nginx.proxy_ssl_enc_certificate_key._value = client_enc_cert_key;
        }

        ret = await waitFlush(common_client);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }

        let common_server = await createNginxConf(GMCONF.NGINX_SERVER_COMMON_CONF);

        if(!common_server.nginx.ssl_certificate) {
            common_server.nginx._add("ssl_certificate", `${server_ssl_cert}`);
        } else {
            common_server.nginx.ssl_certificate._value = `${server_ssl_cert}`;
        }

        let server_cert_key = `engine:tasscard_sm2:${server_cert_index}`;
        if(!common_server.nginx.ssl_certificate_key) {
            common_server.nginx._add("ssl_certificate_key", server_cert_key);
        } else {
            common_server.nginx.ssl_certificate_key._value = server_cert_key;
        }
        if(!common_server.nginx.ssl_enc_certificate) {
            common_server.nginx._add("ssl_enc_certificate", `${server_ssl_enc}`);
        } else {
            common_server.nginx.ssl_enc_certificate._value = `${server_ssl_enc}`;
        }

        let server_enc_cert_key = `engine:tasscard_sm2:${server_enc_cert_index}`;
        if(!common_server.nginx.ssl_enc_certificate_key) {
            common_server.nginx._add("ssl_enc_certificate_key", server_enc_cert_key);
        } else {
            common_server.nginx.ssl_enc_certificate_key._value = server_enc_cert_key;
        }

        common_server.nginx._remove("ssl_session_cache");
        common_server.nginx._remove("ssl_session_timeout");
        common_server.nginx._remove("ssl_ciphers");
        common_server.nginx._remove("ssl_prefer_server_ciphers");

        ret = await waitFlush(common_server);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }
        ret = openssl_lib_source();
        if(ret != 0) {
            console.log("error openssl_lib_source:",ret);
            break;
        }
        ret = nginx_reload();
    }while(0);
    return ret;
}

TassCmd2.close_vpn = async function() {
    let path_conf = GMCONF.NGINX_CLIENT_COMMON_CONF;//path.resolve(GMCONF.DIR_NGINX_CONFS, filename+".conf");
    let ret = 0;
    do{
        if(!fs.existsSync(path_conf) || !fs.existsSync(GMCONF.NGINX_SERVER_COMMON_CONF)) {
            ret = -1;
            break;
        }
        let common_client = await createNginxConf(path_conf);
        common_client.nginx._remove("proxy_ssl");
        common_client.nginx._remove("proxy_ssl_gm");
        common_client.nginx._remove("proxy_ssl_ciphers");
        common_client.nginx._remove("proxy_ssl_certificate");
        common_client.nginx._remove("proxy_ssl_certificate_key");
        common_client.nginx._remove("proxy_ssl_enc_certificate");
        common_client.nginx._remove("proxy_ssl_enc_certificate_key");

        ret = await waitFlush(common_client);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }

        let common_server = await createNginxConf(GMCONF.NGINX_SERVER_COMMON_CONF);
        if(SUPPORT_HTTPS) {
            if(!common_server.nginx.ssl_certificate) {
                common_server.nginx._add("ssl_certificate", GMCONF.HTTPS_PATH_CERTS);
            } else {
                common_server.nginx.ssl_certificate._value = GMCONF.HTTPS_PATH_CERTS;
            }
            if(!common_server.nginx.ssl_certificate_key) {
                common_server.nginx._add("ssl_certificate_key", GMCONF.HTTPS_PATH_CERTS_KEY);
            } else {
                common_server.nginx.ssl_certificate_key._value = GMCONF.HTTPS_PATH_CERTS_KEY;
            }
        
            common_server.nginx._remove("ssl_enc_certificate");
            common_server.nginx._remove("ssl_enc_certificate_key");
        
            if(!common_server.nginx.ssl_session_cache) {
                common_server.nginx._add("ssl_session_cache", "shared:SSL:1m");
            } else {
                common_server.nginx.ssl_session_cache._value = "shared:SSL:1m";
            }
        
            if(!common_server.nginx.ssl_session_timeout) {
                common_server.nginx._add("ssl_session_timeout", "5m");
            } else {
                common_server.nginx.ssl_session_timeout._value = "5m";
            }
        
            if(!common_server.nginx.ssl_ciphers) {
                common_server.nginx._add("ssl_ciphers", "HIGH:!aNULL:!MD5");
            } else {
                common_server.nginx.ssl_ciphers._value = "HIGH:!aNULL:!MD5";
            }
        
            if(!common_server.nginx.ssl_prefer_server_ciphers) {
                common_server.nginx._add("ssl_prefer_server_ciphers", "on");
            } else {
                common_server.nginx.ssl_prefer_server_ciphers._value = "on";
            }
        } else {
            common_server.nginx._remove("ssl_certificate");
            common_server.nginx._remove("ssl_certificate_key");
            common_server.nginx._remove("ssl_enc_certificate");
            common_server.nginx._remove("ssl_enc_certificate_key");
        }

        ret = await waitFlush(common_server);
        if(ret != 0) {
            console.log("error waitFlush:",ret);
            break;
        }
        ret = openssl_lib_source();
        if(ret != 0) {
            console.log("error openssl_lib_source:",ret);
            break;
        }
        ret = nginx_reload();
    }while(0);
    return ret;
}

module.exports = TassCmd2;

// async function testFun() {
//     // let ret = await TassCmd2.add_new_conf(false, "192.168.1.120", "1443", "127.0.0.1", "9000", "9001",
//     // "/root/tasscard_engine/cert/client/sm2/CS_SM2_CARD.crt", 30, "/root/tasscard_engine/cert/client/sm2/CE_SM2_CARD.crt", 31,
//     // "/Users/haikerenwu/Documents/work/carddev/tassl_tengits/express_test/public/certs/SS_SM2_CARD_bak.crt", 20,
//     // "/Users/haikerenwu/Documents/work/carddev/tassl_tengits/express_test/public/certs/SE_SM2_CARD_bak.crt", 21);

//     // let ret1 = await TassCmd2.edit_conf("1596594046862_999", "192.168.1.121", "1444", "9003");
//     // let ret2 = await TassCmd2.del_conf("1596594138614_117");
//     let ret3 = await TassCmd2.start_vpn("/root/tasscard_engine/cert/client/sm2/CS_SM2_CARD.crt", 30, "/root/tasscard_engine/cert/client/sm2/CE_SM2_CARD.crt", 31,
//     "/Users/haikerenwu/Documents/work/carddev/tassl_tengits/express_test/public/certs/SS_SM2_CARD_bak.crt", 20,
//     "/Users/haikerenwu/Documents/work/carddev/tassl_tengits/express_test/public/certs/SE_SM2_CARD_bak.crt", 21);
//     //let ret4 = await TassCmd2.close_vpn();
//     let ret = await TassCmd2.query_vpn_stat();
//     console.log(ret);
// }

// testFun();
